package roles

import (
	"context"
	"fmt"
	"sync"
	"time"

	"xagent/internal/actor"
	"xagent/internal/memory"
	"xagent/internal/providers"
	proto "xagent/proto/v1"

	"github.com/sirupsen/logrus"
)

// RoleState represents the state of a role
type RoleState int

const (
	StateIdle RoleState = iota
	StateThinking
	StatePlanning
	StateExecuting
	StateWaiting
	StateFinished
)

// RoleContext represents the runtime context for a role
type RoleContext struct {
	memory       memory.MemoryManager
	state        RoleState
	currentTask  *proto.Task
	actions      map[string]actor.IAction
	capabilities []string
	mutex        sync.RWMutex
}

// Message represents a message in the system
type Message struct {
	ID        string
	Type      string
	Content   string
	Metadata  map[string]string
	CreatedAt time.Time
	UpdatedAt time.Time
}

// Role defines the interface for all team roles
type Role interface {
	// GetID returns the role's unique identifier
	GetID() string

	// GetRole returns the role's name/type
	GetRole() string

	// GetDescription returns the role's description
	GetDescription() string

	// GetCapabilities returns the role's capabilities
	GetCapabilities() []string

	// ProcessTask processes a task specific to this role
	ProcessTask(ctx context.Context, msg *Message) (*Message, error)

	// Think generates thoughts about the current task
	Think(ctx context.Context, input string) (*actor.Thought, error)

	// Plan creates a plan for executing the current task
	Plan(ctx context.Context) error

	// Execute executes the current plan
	Execute(ctx context.Context) error

	// GetState returns the current state of the role
	GetState() RoleState

	// SetState sets the state of the role
	SetState(state RoleState)
}

// BaseRole provides a base implementation of the Role interface
type BaseRole struct {
	id          string
	name        string
	role        string
	description string
	llm         providers.LLMProvider
	rc          *RoleContext
	logger      *logrus.Logger
}

// NewBaseRole creates a new base role
func NewBaseRole(id, name, role, description string) *BaseRole {
	return &BaseRole{
		id:          id,
		name:        name,
		role:        role,
		description: description,
		rc: &RoleContext{
			memory:       memory.NewInMemoryManager(),
			state:        StateIdle,
			actions:      make(map[string]actor.IAction),
			capabilities: make([]string, 0),
		},
		logger: logrus.New(),
	}
}

// GetID returns the role's unique identifier
func (r *BaseRole) GetID() string {
	return r.id
}

// GetRole returns the role's name/type
func (r *BaseRole) GetRole() string {
	return r.role
}

// GetDescription returns the role's description
func (r *BaseRole) GetDescription() string {
	return r.description
}

// GetCapabilities returns the role's capabilities
func (r *BaseRole) GetCapabilities() []string {
	r.rc.mutex.RLock()
	defer r.rc.mutex.RUnlock()
	return append([]string{}, r.rc.capabilities...)
}

// ProcessTask processes a task specific to this role
func (r *BaseRole) ProcessTask(ctx context.Context, msg *Message) (*Message, error) {
	r.rc.mutex.Lock()
	r.rc.currentTask = &proto.Task{
		Id:          msg.ID,
		Name:        msg.Type,
		Description: msg.Content,
	}
	r.rc.mutex.Unlock()

	// Execute the task processing pipeline
	if err := r.Plan(ctx); err != nil {
		return nil, fmt.Errorf("failed to plan task: %w", err)
	}

	if err := r.Execute(ctx); err != nil {
		return nil, fmt.Errorf("failed to execute task: %w", err)
	}

	return &Message{
		ID:      msg.ID,
		Type:    "response",
		Content: "Task completed successfully",
		Metadata: map[string]string{
			"status": "completed",
		},
		CreatedAt: time.Now(),
		UpdatedAt: time.Now(),
	}, nil
}

// Think generates thoughts about the current task
func (r *BaseRole) Think(ctx context.Context, input string) (*actor.Thought, error) {
	r.SetState(StateThinking)
	defer r.SetState(StateIdle)

	// Build thinking prompt
	thinkingPrompt := fmt.Sprintf(`As a %s, analyze the following task and provide your thoughts:

Task: %s

Please structure your response as follows:
1. Initial thoughts and understanding
2. Reasoning process
3. Action plan (as a list of steps)
4. Potential issues or criticisms
5. Next immediate action to take

Consider your role's responsibilities and constraints while analyzing.`, r.role, input)

	// Use LLM to generate thought
	response, err := r.llm.Complete(ctx, thinkingPrompt, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to think: %w", err)
	}

	// Parse response into thought structure
	thought := &actor.Thought{
		Content:    response,
		Reasoning:  "Parsed reasoning",
		Plan:       []string{"Step 1", "Step 2"},
		Critical:   "Potential issues",
		NextAction: "Next step",
		Timestamp:  time.Now(),
	}

	return thought, nil
}

// Plan creates a plan for executing the current task
func (r *BaseRole) Plan(ctx context.Context) error {
	r.SetState(StatePlanning)
	defer r.SetState(StateIdle)

	r.rc.mutex.RLock()
	task := r.rc.currentTask
	r.rc.mutex.RUnlock()

	if task == nil {
		return fmt.Errorf("no task to plan")
	}

	// Generate plan using LLM
	thought, err := r.Think(ctx, fmt.Sprintf("Plan execution for task: %s", task.Description))
	if err != nil {
		return fmt.Errorf("failed to generate plan: %w", err)
	}

	// Store plan in memory
	if err := r.rc.memory.Store(ctx, &memory.MemoryEntry{
		ID:      task.Id,
		Type:    "plan",
		Content: thought.Content,
		Metadata: map[string]interface{}{
			"plan":      thought.Plan,
			"reasoning": thought.Reasoning,
		},
		CreatedAt: time.Now(),
		UpdatedAt: time.Now(),
	}); err != nil {
		return fmt.Errorf("failed to store plan: %w", err)
	}

	return nil
}

// Execute executes the current plan
func (r *BaseRole) Execute(ctx context.Context) error {
	r.SetState(StateExecuting)
	defer r.SetState(StateIdle)

	r.rc.mutex.RLock()
	task := r.rc.currentTask
	r.rc.mutex.RUnlock()

	if task == nil {
		return fmt.Errorf("no task to execute")
	}

	// Get plan from memory
	entry, err := r.rc.memory.Get(ctx, task.Id)
	if err != nil {
		return fmt.Errorf("failed to get plan: %w", err)
	}

	plan, ok := entry.Metadata["plan"].([]string)
	if !ok {
		return fmt.Errorf("invalid plan format")
	}

	// Execute each step in the plan
	for _, step := range plan {
		if err := r.executeStep(ctx, step); err != nil {
			return fmt.Errorf("failed to execute step: %w", err)
		}
	}

	return nil
}

// executeStep executes a single step in the plan
func (r *BaseRole) executeStep(ctx context.Context, step string) error {
	// Think about how to execute this step
	thought, err := r.Think(ctx, fmt.Sprintf("Execute step: %s", step))
	if err != nil {
		return fmt.Errorf("failed to think about step execution: %w", err)
	}

	// Find appropriate action for this step
	action := r.findActionForStep(thought.NextAction)
	if action == nil {
		return fmt.Errorf("no action found for step: %s", step)
	}

	// Execute the action
	_, err = action.Run(ctx, step)
	if err != nil {
		return fmt.Errorf("failed to execute action: %w", err)
	}

	return nil
}

// findActionForStep finds the appropriate action for a step
func (r *BaseRole) findActionForStep(actionName string) actor.IAction {
	r.rc.mutex.RLock()
	defer r.rc.mutex.RUnlock()

	action, ok := r.rc.actions[actionName]
	if !ok {
		return nil
	}

	return action
}

// GetState returns the current state of the role
func (r *BaseRole) GetState() RoleState {
	r.rc.mutex.RLock()
	defer r.rc.mutex.RUnlock()
	return r.rc.state
}

// SetState sets the state of the role
func (r *BaseRole) SetState(state RoleState) {
	r.rc.mutex.Lock()
	defer r.rc.mutex.Unlock()
	r.rc.state = state
}

// AddAction adds an action to the role
func (r *BaseRole) AddAction(action actor.IAction) {
	r.rc.mutex.Lock()
	defer r.rc.mutex.Unlock()
	r.rc.actions[action.Name()] = action
}

// AddCapability adds a capability to the role
func (r *BaseRole) AddCapability(capability string) {
	r.rc.mutex.Lock()
	defer r.rc.mutex.Unlock()
	r.rc.capabilities = append(r.rc.capabilities, capability)
}
